package com.dingreading.cloud.admin.service.impl;

import com.dingreading.cloud.admin.dto.CourseTrainLogDto;
import com.dingreading.cloud.admin.dto.StoreForTrainLogDto;
import com.dingreading.cloud.admin.entity.CourseTrain;
import com.dingreading.cloud.admin.entity.CourseTrainFile;
import com.dingreading.cloud.admin.entity.CourseTrainTypes;
import com.dingreading.cloud.admin.entity.CourseTrainVideoLog;
import com.dingreading.cloud.admin.service.*;
import com.dingreading.cloud.admin.util.CdnUrlUtil;
import com.dingreading.cloud.admin.util.CourseTrainUtil;
import com.dingreading.cloud.common.base.R;
import com.dingreading.cloud.common.util.*;
import com.mybatisflex.core.paginate.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CourseTrainCommonServiceImpl implements CourseTrainCommonService {
    @Resource
    private CdnUrlUtil cdnUrlUtil;
    @Resource
    private StoresService storesService;
    @Resource
    private CourseTrainService courseTrainService;
    @Resource
    private CourseTrainTypesService courseTrainTypesService;
    @Resource
    private CourseTrainFileService courseTrainFileService;
    @Resource
    private CourseTrainLogService courseTrainLogService;
    @Resource
    private CourseTrainVideoLogService courseTrainVideoLogService;

    @Override
    public R<Object> pageList(PageUtil pageUtil, String headStaffUid, String headStaffName, String keyword, String mainTypeUid, String typeUid, Integer enabled) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        Page<CourseTrain> page = courseTrainService.pageList(pageUtil, mainTypeUid, typeUid, keyword, enabled);

        PageDto<CourseTrain> courseTrainPageDto = new PageDto<>();
        courseTrainPageDto.setTotal(page.getTotalRow());
        courseTrainPageDto.setList(page.getRecords());
        return R.ok(courseTrainPageDto);
    }

    @Override
    public R<Object> pageFileList(PageUtil pageUtil, String headStaffUid, String headStaffName, String keyword, String lessonUid, Integer enabled) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        Page<CourseTrainFile> page = courseTrainFileService.pageFileList(pageUtil, lessonUid, keyword, enabled);
        PageDto<CourseTrainFile> trainFilePageDto = new PageDto<>();
        trainFilePageDto.setTotal(page.getTotalRow());
        trainFilePageDto.setList(page.getRecords());
        return R.ok(trainFilePageDto);
    }

    @Override
    public R<Object> newFile(String headStaffUid, String headStaffName, String uid, String lessonUid, String name, String url, Integer videoDuration) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(name))
            return R.fail("请输入名称");
        if (StringUtils.isBlank(lessonUid))
            return R.fail("请选择课程");
        if (StringUtils.isBlank(url))
            return R.fail("请上传文件");

        Integer fileType = null;
        if (url.endsWith(".mp4") || url.endsWith(".MP4"))
            fileType = 0;
        if (url.endsWith(".pdf") || url.endsWith(".PDF"))
            fileType = 1;
        CourseTrain courseTrain = courseTrainService.getByUid(lessonUid);
        if (courseTrain == null) return R.fail("课程获取失败");

        if (StringUtils.isBlank(uid)) {
            // 新增
            CourseTrainFile courseTrainFile = CourseTrainUtil.newTrainFile(name, url, lessonUid, fileType, videoDuration, headStaffUid, headStaffName);
            boolean updated = courseTrainFileService.saveOrUpdate(courseTrainFile);
            if (!updated) return R.fail("保存失败");
        } else {
            // 修改
            CourseTrainFile courseTrainFile = courseTrainFileService.getByUid(uid);
            if (courseTrainFile == null) return R.fail("获取失败");

            if (StringUtils.isNotBlank(name))
                courseTrainFile.setName(name);
            if (StringUtils.isNotBlank(url))
                courseTrainFile.setUrl(url);
            if (fileType != null)
                courseTrainFile.setFileType(fileType);
            courseTrainFile.setVideoDuration(NepUtil.nullToZero(videoDuration));
            courseTrainFile.setLastStaffUid(headStaffUid);
            courseTrainFile.setLastStaffName(headStaffName);
            boolean updated = courseTrainFileService.saveOrUpdate(courseTrainFile);
            if (!updated) return R.fail("修改失败");
        }
        return R.ok();
    }

    @Override
    public R<Object> getFileByUid(String headStaffUid, String headStaffName, String uid) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(uid))
            return R.fail("请选择文件");

        CourseTrainFile courseTrainFile = courseTrainFileService.getByUid(uid);
        return R.ok(courseTrainFile);
    }

    @Override
    public R<Object> updateFileEnabled(String headStaffUid, String headStaffName, String uid, Integer enabled) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(uid))
            return R.fail("请选择文件");
        if (enabled == null)
            return R.fail("请选择状态");
        CourseTrainFile courseTrainFile = courseTrainFileService.getByUid(uid);
        if (courseTrainFile == null) return R.fail("获取失败");
        courseTrainFile.setEnabled(enabled);
        boolean updated = courseTrainFileService.saveOrUpdate(courseTrainFile);
        return updated ? R.ok() : R.fail("修改失败");
    }

    @Override
    public R<Object> getFileByLessonUid(String headStaffUid, String headStaffName, String lessonUid) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(lessonUid))
            return R.fail("请选择课程");

        CourseTrain courseTrain = courseTrainService.getByUid(lessonUid);
        if (courseTrain == null) return R.fail("课程获取失败");

        List<CourseTrainFile> courseTrainFiles = courseTrainFileService.getByLessonUid(lessonUid);
        return R.ok(courseTrainFiles);
    }

    @Override
    public R<Object> newBean(String headStaffUid, String headStaffName, String uid, String name, String intro, String mainTypeUid, String typeUid) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(name))
            return R.fail("请输入名称");

        if (StringUtils.isBlank(mainTypeUid))
            return R.fail("请选择主分类");
        if (StringUtils.isBlank(typeUid))
            return R.fail("请选择关联分类");

        if (StringUtils.isBlank(uid)) {
            // 新增
            CourseTrainTypes mainType = courseTrainTypesService.getInfoByUid(mainTypeUid);
            if (mainType == null) return R.fail("主分类获取失败");
            CourseTrainTypes parentType = courseTrainTypesService.getInfoByUid(typeUid);
            if (parentType == null) return R.fail("关联分类获取失败");
            if (!parentType.getLevelEnd().equals(1)) return R.fail("分类不是最后一层不能新增");

            // 获取最大的序号
            Integer maxOrderNum = courseTrainService.getMaxShowOrder(typeUid) + 1;

            CourseTrain courseTrain = CourseTrainUtil.newCourseTrainBean(
                    name, intro, maxOrderNum, mainTypeUid, typeUid, parentType.getNodes(), headStaffUid, headStaffName
            );

            boolean b = courseTrainService.saveOrUpdate(courseTrain);
            if (!b) {
                return R.fail("保存失败");
            }
        } else {
            // 修改
            CourseTrain courseTrain = courseTrainService.getByUid(uid);
            if (courseTrain == null) return R.fail("课程获取失败");

            if (name != null)
                courseTrain.setName(name);
            if (intro != null)
                courseTrain.setIntro(intro);
            courseTrain.setLastStaffUid(headStaffUid);
            courseTrain.setLastStaffName(headStaffName);
            boolean b = courseTrainService.saveOrUpdate(courseTrain);
            if (!b) return R.fail("修改失败");
        }

        return R.ok();
    }

    @Override
    public R<Object> getByUid(String headStaffUid, String headStaffName, String uid) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(uid))
            return R.fail("请选择课程");
        CourseTrain courseTrain = courseTrainService.getByUid(uid);
        return R.ok(courseTrain);
    }

    @Override
    public R<Object> updateEnabled(String headStaffUid, String headStaffName, String uid, Integer enabled) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(uid))
            return R.fail("请选择课程");
        if (enabled == null)
            return R.fail("请选择状态");
        CourseTrain courseTrain = courseTrainService.getByUid(uid);
        if (courseTrain == null) return R.fail("课程获取失败");
        courseTrain.setEnabled(enabled);
        boolean b = courseTrainService.saveOrUpdate(courseTrain);
        return b ? R.ok() : R.fail("修改失败");
    }

    @Override
    public R<Object> upOrDown(String headStaffUid, String headStaffName, String uid, Integer type) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(uid))
            return R.fail("请选择课程");
        if (type == null)
            return R.fail("请选择移动状态");

        CourseTrain data = courseTrainService.getByUid(uid);
        if (data == null) return R.fail("获取失败");
        if (type.equals(1)) {
            // 上移
            CourseTrain preData = courseTrainService.getLtOrderNum(data.getTypeUid(), data.getOrderNum());
            if (preData == null) return R.fail("已在最前");
            // 和前一条记录的序号互换
            Integer prevOrderNum = preData.getOrderNum();
            Integer curOrderNum = data.getOrderNum();
            // 序号互换
            boolean updatedPre = courseTrainService.updateOrderNum(preData.getId(), curOrderNum);
            boolean updated = courseTrainService.updateOrderNum(data.getId(), prevOrderNum);
            if (!updatedPre || !updated) return R.fail("修改失败");
        } else if (type.equals(2)) {
            // 下移
            CourseTrain backData = courseTrainService.getGtOrderNum(data.getTypeUid(), data.getOrderNum());
            if (backData == null) return R.fail("已在最后");
            // 和后一条记录的序号互换
            Integer backOrderNum = backData.getOrderNum();
            Integer curOrderNum = data.getOrderNum();
            // 序号互换
            boolean updatedBack = courseTrainService.updateOrderNum(backData.getId(), curOrderNum);
            boolean updated = courseTrainService.updateOrderNum(data.getId(), backOrderNum);
            if (!updatedBack || !updated) return R.fail("修改失败");
        } else {
            return R.fail("类型错误");
        }
        return R.ok();

    }

    @Override
    public R<Object> dropBean(String headStaffUid, String headStaffName, String uid) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(uid))
            return R.fail("请选择课程");
        // 修改
        CourseTrain data = courseTrainService.getByUid(uid);
        if (null == data) return R.fail("获取失败");

        List<CourseTrainFile> courseTrainFileList = courseTrainFileService.getByLessonUid(data.getUid());
        if (CollectionUtils.isNotEmpty(courseTrainFileList))
            return R.fail("该课程已经上传视频");

        boolean updated = courseTrainService.removeById(data);
        if (!updated) return R.fail("删除失败");

        return R.ok();
    }

    @Override
    public R<String> getCdnUrl(HttpServletRequest request, String headStaffUid, String headStaffName, String url) {
        if (StringUtils.isBlank(headStaffUid))
            return R.fail("请选择总部员工");
        if (StringUtils.isBlank(url))
            return R.fail("请选择课程地址");

        R<String> cdnUrl = cdnUrlUtil.getCdnUrl(request, url);
        return cdnUrl;
    }

    @Override
    public R<Object> getList(String headStaffUid, String headStaffName, String storeUid, String beginDate, String endDate) throws ParseException {
        if (StringUtils.isBlank(beginDate))
            return R.fail("请选择开始日期");
        if (StringUtils.isBlank(endDate))
            return R.fail("请选择结束日期");

        Date beginTime = DateUtil.parseDate(beginDate);
        Date endTime = DateUtil.parseDate(endDate);
        long compareDate = DateUtil.differentDays(beginTime, endTime);
        if (compareDate > 90)
            return R.fail("选择周期不能超过90天");

        // 3. 查询聚合后的学习日志
        List<CourseTrainLogDto> aggregatedLogs = courseTrainLogService.getAggregatedLogs(storeUid, beginDate, endDate);
        if (CollectionUtils.isEmpty(aggregatedLogs))
            return R.ok(new ArrayList<>());

        // 4. 获取文件UID列表
        List<String> fileUids = aggregatedLogs.stream()
                .filter(log -> log.getType() == 1)
                .map(CourseTrainLogDto::getFileUid).distinct().collect(Collectors.toList());
        List<String> staffUids = aggregatedLogs.stream()
                .filter(log -> log.getType() == 1)
                .map(CourseTrainLogDto::getStaffUid).distinct().collect(Collectors.toList());

        Map<List<Object>, List<CourseTrainVideoLog>> videoLogsMap = courseTrainVideoLogService.getAllVideoLogs(staffUids, fileUids, beginDate, endDate);

        Map<String, Integer> fileVideosMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(fileUids))
            fileVideosMap = courseTrainFileService.getFileVideosDurations(fileUids);
        // 4. 遍历聚合日志，处理视频类型的记录
        for (CourseTrainLogDto dto : aggregatedLogs) {

            // 如果是视频类型，则查询详细播放日志并计算时长
            if (dto.getType() == 1) {
                List<CourseTrainVideoLog> videoLogs = videoLogsMap.get(
                        Arrays.asList(
                                dto.getCorpUid(), dto.getStoreUid(), dto.getStaffUid(),
                                dto.getCourseTrainUid(), dto.getFileUid()
                        )
                );

                int totalDuration = 0;
                int effectiveDuration = 0;
                String rateFormatted = "0%";
                if (CollectionUtils.isNotEmpty(videoLogs)) {
                    // 计算总观看时长
                    totalDuration = videoLogs.stream().mapToInt(CourseTrainVideoLog::getDuration).sum();
                    // 计算有效观看时长
                    effectiveDuration = calculateEffectiveDuration(videoLogs);
                    Integer videoDuration = fileVideosMap.get(dto.getFileUid());
                    if (videoDuration == null || videoDuration == 0)
                        dto.setRate("0%");
                    double rate = DoubleUtil.div(effectiveDuration, videoDuration, 2);
                    //将结果转成百分比
                    rateFormatted = String.format("%.0f%%", rate * 100);
                }
                dto.setTotalDuration(String.valueOf(totalDuration));
                dto.setEffectiveDuration(String.valueOf(effectiveDuration));
                dto.setRate(rateFormatted);
            }
        }

        return R.ok(aggregatedLogs);
    }

    @Override
    public R<Object> getStores(String headStaffUid, String headStaffName, String beginDate, String endDate) throws ParseException {
        if (StringUtils.isBlank(beginDate))
            return R.fail("请选择开始日期");
        if (StringUtils.isBlank(endDate))
            return R.fail("请选择结束日期");

        Date beginTime = DateUtil.parseDate(beginDate);
        Date endTime = DateUtil.parseDate(endDate);
        long compareDate = DateUtil.differentDays(beginTime, endTime);
        if (compareDate > 90)
            return R.fail("选择周期不能超过90天");

        List<String> storeUids = courseTrainLogService.getStores(beginDate, endDate);
        if (CollectionUtils.isEmpty(storeUids))
            return R.ok(new ArrayList<>());
        List<StoreForTrainLogDto> stores = storesService.getStores(storeUids);
        return R.ok(stores);
    }

    /**
     * 计算有效观看时长
     * 核心逻辑：合并重叠的时间区间
     *
     * @param videoLogs 已按startTime排序的视频日志列表
     * @return 有效时长（秒）
     */
    private int calculateEffectiveDuration(List<CourseTrainVideoLog> videoLogs) {
        if (videoLogs == null || videoLogs.isEmpty()) {
            return 0;
        }

        // 使用 LinkedList 便于访问和修改最后一个元素
        LinkedList<int[]> mergedIntervals = new LinkedList<>();

        for (CourseTrainVideoLog log : videoLogs) {
            int[] currentInterval = {log.getStartTime(), log.getEndTime()};

            // 如果合并列表为空，或者当前区间与上一个合并后的区间不重叠，则直接添加
            if (mergedIntervals.isEmpty() || currentInterval[0] > mergedIntervals.getLast()[1]) {
                mergedIntervals.add(currentInterval);
            } else {
                // 如果重叠，则合并：更新上一个区间的结束时间
                mergedIntervals.getLast()[1] = Math.max(mergedIntervals.getLast()[1], currentInterval[1]);
            }
        }

        // 计算合并后所有区间的总长度
        int totalEffectiveDuration = 0;
        for (int[] interval : mergedIntervals) {
            totalEffectiveDuration += (interval[1] - interval[0]);
        }

        return totalEffectiveDuration;
    }

}
